/*
 * $Id: CanDIActionResolver.java 6 2009-03-20 00:57:43Z scotthernandez $
 * $URL: https://condi-stripes.googlecode.com/svn/ConDI-Stripes/trunk/src/com/googlecode/condistripes/CanDIActionResolver.java $
 *
 * Copyright 2008-2009 Scott Hernandez
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.googlecode.candistripes;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.controller.ActionBeanContextFactory;
import net.sourceforge.stripes.controller.ActionResolver;
import net.sourceforge.stripes.controller.NameBasedActionResolver;
import net.sourceforge.stripes.util.Log;

/**
 * An {@link ActionResolver} and {@link ActionBeanContextFactory} that creates (gets) classes from the (@link Manager)
 * This should be configured as params to the stripes servlet filter.
 * <pre>
 * 		<init-param>
 *	       <param-name>ActionResolver.Class</param-name>
 *	       <param-value>com.googlecode.candistripes.CanDIHelper</param-value> 
 *    	</init-param>
 *		<init-param>
 *	       <param-name>ActionBeanContextFactory.Class</param-name>
 *	       <param-value>com.googlecode.candistripes.CanDIHelper</param-value> 
 *    	</init-param>
 * </pre>
 * 
 * @author Scott Hernandez
 * @author Jeff Schnitzer
 */
public class CanDIHelper extends NameBasedActionResolver implements ActionBeanContextFactory {

	/** */
	private Log log = Log.getInstance(CanDIHelper.class);

	/** The bean manager for our container, whatever it may be */
	protected BeanManager beanMan = BeanManagerFinder.getBeanManager();
	
	/** */
    public CanDIHelper() {
    	log.debug("ctor: " + CanDIHelper.class.toString());
    	
    	// First thing is we need to replace the eventMappings map with something
    	// that also looks up parent classes in the map.  The problem is that Resin
    	// (and possibly other servers that use code generation) derives a new
    	// generated class when you add aspects like @TransactionAttribute,
    	// and these new generated classes are not registered in the eventMappings.
    	try
    	{
    		Field eventMappingsField = this.getClass().getSuperclass().getSuperclass().getDeclaredField("eventMappings");
    		eventMappingsField.setAccessible(true);
    		
			Map<Class<? extends ActionBean>, Map<String, Method>> eventMappings =
				new HashMap<Class<? extends ActionBean>, Map<String, Method>>() {
					private static final long serialVersionUID = 1L;

					@SuppressWarnings("unchecked")
					@Override
					public Map<String, Method> get(Object key) {
					    Map<String, Method> value = super.get(key);
					    if (value != null)
					        return value;

						Class sup = ((Class)key).getSuperclass();
						if (sup != null)
							value = super.get(sup);
						
						if (value == null)
					        return Collections.emptyMap();
					    else
					    	return value;
					}
				};
    	    
    	    eventMappingsField.set(this, eventMappings);
    	}
    	catch (Exception ex) { throw new RuntimeException(ex); }
	}
	
    /* (non-Javadoc)
     * @see net.sourceforge.stripes.controller.AnnotatedClassActionResolver#makeNewActionBean(java.lang.Class, net.sourceforge.stripes.action.ActionBeanContext)
     */
	@Override
	protected ActionBean makeNewActionBean(
			Class<? extends ActionBean> type, ActionBeanContext context) throws Exception {
		
		return (ActionBean)this.get(type);
	}

	/* (non-Javadoc)
	 * @see net.sourceforge.stripes.controller.ActionBeanContextFactory#getContextInstance(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	public ActionBeanContext getContextInstance(
			HttpServletRequest req, HttpServletResponse res) throws ServletException {

		ActionBeanContext ctx = (ActionBeanContext)this.get(ActionBeanContext.class);
		ctx.setRequest(req);
		ctx.setResponse(res);
		return ctx;
	}
	
	/** Helper.  This process is defined by the SPI of the JSR299 spec. */
	protected Object get(Class<?> type)
	{
		log.debug("Getting instance of: ", type.toString());
		
		Set<Bean<?>> beans = this.beanMan.getBeans(type);
		
		log.debug("Found: ", beans);
		
		Bean<?> bean = this.beanMan.getHighestPrecedenceBean(beans);
		
		log.debug("Best: ", bean);
		
		Object found = this.beanMan.getReference(bean, type, null);
		
		log.debug("Got: ", found);
		
		return found;
	}
}